package com.hwy.chart.coordinate.axis

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
import androidx.annotation.IntDef
import androidx.annotation.MainThread

/**
 * =======================================
 * 作者: hewenyu
 * 日期: 2022/5/10 18:41
 * 说明: 坐标轴基类，坐标轴原点为左下角
 * =======================================
 */
abstract class BaseAxis<T> {

    companion object {

        /**
         * 坐标轴无效的宽度
         */
        const val INVALID_AXIS_WIDTH = -1F

        /**
         * 坐标轴为横轴 (x轴)
         */
        const val HORIZONTAL = 1

        /**
         * 坐标轴为纵轴 (y轴)
         */
        const val VERTICAL = 2

        @IntDef(HORIZONTAL, VERTICAL)
        annotation class Orientation

    }

    protected val axisPaint by lazy {
        Paint().apply {
            isDither = true
            isAntiAlias = true
            style = Paint.Style.FILL_AND_STROKE
        }
    }

    /**
     * 坐标轴线条粗细
     */
    var axisWidth = 4F

    /**
     * 坐标轴线条颜色
     */
    @ColorInt
    var axisColor = Color.GRAY

    /**
     * 坐标轴显示的位置
     * 坐标原点为 左下角
     *
     * 横轴: 横轴在整个绘制区域的纵向位置百分比
     * 纵轴: 纵轴在整个绘制区域的横向位置百分比
     */
    @FloatRange(from = 0.0, to = 1.0)
    var axisSite = 0F

    /**
     * 坐标轴最大值
     */
    var maxValue: T? = null

    /**
     * 坐标轴最小值
     */
    var minValue: T? = null

    /**
     * 坐标系的Tag标签绘制对象
     */
    var axisTag = AxisTag()

    @Orientation
    protected var orientation = HORIZONTAL

    /**
     * 坐标轴数据集
     */
    protected val dataList: ArrayList<T?> = ArrayList()

    init {
        initAxis()
    }

    /**
     * 初始化坐标轴相关信息
     */
    protected abstract fun initAxis()

    fun getSnapshot(): String {
        return dataList.toString()
    }

    // region ------------------------------ 坐标轴极值计算 ------------------------------

    /**
     * 计算坐标轴最大值
     */
    protected open fun calculateMaxValue() {
        // 由子类实现
    }

    /**
     * 计算坐标轴最小值
     */
    protected open fun calculateMinValue() {
        // 由子类实现
    }

    // endregion ----------------------------------------------------------------------

    // region ------------------------------ 坐标轴绘制相关 ------------------------------

    /**
     * 计算坐标轴显示的坐标位置
     *
     * 横轴(x轴): 返回横轴显示的y坐标
     * 纵轴(y轴): 返回纵轴显示的x坐标
     */
    protected

    abstract fun calculateCoordinate(bounds: RectF): Float

    /**
     * 绘制坐标轴
     */
    open fun draw(canvas: Canvas, bounds: RectF) {
        if (axisWidth != INVALID_AXIS_WIDTH) {
            // 绘制坐标轴
            drawAxis(canvas, bounds)
        }
        // 绘制坐标轴标签
        drawAxisTag(canvas, bounds)
    }

    /**
     * 绘制坐标轴
     */
    protected abstract fun drawAxis(canvas: Canvas, bounds: RectF)

    /**
     * 绘制坐标轴标签
     */
    protected open fun drawAxisTag(canvas: Canvas, bounds: RectF) {
        axisTag.draw(canvas, bounds, orientation, calculateCoordinate(bounds))
    }

    // endregion -----------------------------------------------------------------------

    // region ------------------------------ 坐标轴位置计算相关 ------------------------------

    /**
     * 更新坐标轴数据集
     */
    @MainThread
    fun updateAxisData(list: ArrayList<T?>?) {
        dataList.clear()
        list?.run {
            dataList.addAll(this)
        }
    }

    /**
     * 根据坐标系中的坐标计算出[dataList]中对应的数据坐标
     *
     * @param coordinate 图表坐标系中的坐标，该坐标可能会溢出 [bounds]的范围
     * @param bounds 图表中可以绘制的区域
     */
    abstract fun getIndexByCoordinate(coordinate: Float?, bounds: RectF): Int?

    /**
     * 根据[dataList]中指定的index计算出在图表中对应的坐标信息
     *
     * @param index 数据集中需要计算坐标的index
     * @param bounds 图表中可以绘制的区域
     */
    abstract fun getCoordinateByIndex(index: Int, bounds: RectF): Float?

    /**
     * 根据坐标系中的坐标获取[dataList]中对应index的数据
     */
    open fun getDataByCoordinate(coordinate: Float?, bounds: RectF): T? {
        return coordinate?.let {
            getIndexByCoordinate(it, bounds)?.run {
                if (this >= 0 && this < dataList.size) {
                    return dataList[this]
                }
                return null
            }
        }
    }

    /**
     * 调整坐标轴上任意的坐标值为其对应数据点的坐标
     *
     * @param coordinate 实际touch事件的坐标值
     * @param bounds 图表可绘制区域
     */
    open fun adjustCoordinate(coordinate: Float, bounds: RectF): Float? {
        // 1.将任意坐标转换成其对应的数据的index
        // 2.将index对应的数据转成对应的坐标
        return getIndexByCoordinate(coordinate, bounds)?.let { index ->
            getCoordinateByIndex(index, bounds)
        }
    }

    // endregion --------------------------------------------------------------------------

}